package gradle.common.core.exception;

import gradle.common.core.response.BaseResponse;
import gradle.common.core.enums.ResultCodeEnum;
import jakarta.servlet.http.HttpServletResponse;
import jakarta.validation.ConstraintViolationException;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.apache.commons.lang3.exception.ExceptionUtils;
import org.springframework.beans.TypeMismatchException;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.http.HttpStatus;
import org.springframework.validation.FieldError;
import org.springframework.web.bind.MethodArgumentNotValidException;
import org.springframework.web.bind.annotation.ControllerAdvice;
import org.springframework.web.bind.annotation.ExceptionHandler;
import org.springframework.web.bind.annotation.ResponseBody;

@Slf4j
@ResponseBody
@ControllerAdvice("org.example")
public class CorelExceptionHandler {

    /**
     * 是否打印堆栈给前端
     */
    @Value("${response.traceException:false}")
    private boolean traceException;
    /**
     * 未知异常捕获
     *
     * @param response 响应
     * @param e        异常
     * @return 标准返回值
     */
    @ExceptionHandler(Exception.class)
    public BaseResponse otherExceptionHandler(HttpServletResponse response, Exception e) {
        response.setStatus(HttpStatus.INTERNAL_SERVER_ERROR.value());
        log.error(e.getMessage(), e);
        return new BaseResponse(ResultCodeEnum.OTHER_EXCEPTION.getCode(), this.getExceptionMessage(e, ResultCodeEnum.OTHER_EXCEPTION), this.getTrackExceptionMessage(e));
    }


    /**
     * 非法参数异常捕获
     *
     * @param response 响应
     * @param e        异常
     * @return 标准返回值
     */
    @ExceptionHandler(IllegalArgumentException.class)
    public BaseResponse onIllegalArgumentExceptionHandler(HttpServletResponse response, IllegalArgumentException e) {
        response.setStatus(HttpStatus.BAD_REQUEST.value());
        log.error(e.getMessage(), e);
        return new BaseResponse(ResultCodeEnum.PARAMETER_ERROR.getCode(), this.getExceptionMessage(e, ResultCodeEnum.PARAMETER_ERROR), this.getTrackExceptionMessage(e));
    }

    /**
     * InvalidBearerTokenException
     *
     * @param response 响应
     * @param e        异常
     * @return 标准返回值
     */
    @ExceptionHandler(InvalidBearerTokenException.class)
    public BaseResponse invalidBearerTokenExceptionHandler(HttpServletResponse response, InvalidBearerTokenException e) {
        response.setStatus(HttpStatus.OK.value());
        log.error(e.getMessage(), e);
        return new BaseResponse(ResultCodeEnum.ACCOUNT_EXPIRED.getCode(), this.getExceptionMessage(e, ResultCodeEnum.ACCOUNT_EXPIRED), this.getTrackExceptionMessage(e));
    }


    /**
     * 参数类型不匹配异常捕获
     *
     * @param response 响应
     * @param e        异常
     * @return 标准返回值
     */
    @ExceptionHandler(TypeMismatchException.class)
    public BaseResponse typeMismatchExceptionHandler(HttpServletResponse response, TypeMismatchException e) {
        response.setStatus(HttpStatus.BAD_REQUEST.value());
        log.error(e.getMessage(), e);
        return new BaseResponse(ResultCodeEnum.PARAMETER_ERROR.getCode(), this.getExceptionMessage(e, ResultCodeEnum.PARAMETER_ERROR), this.getTrackExceptionMessage(e));
    }


    /**
     * 业务异常捕获
     *
     * @param response 响应
     * @param e        异常
     * @return 标准返回值
     */
    @ExceptionHandler(BusinessException.class)
    public BaseResponse businessExceptionHandler(HttpServletResponse response, BusinessException e) {
        response.setStatus(HttpStatus.OK.value());
        log.error(e.getMessage(), e);
        return new BaseResponse(ResultCodeEnum.BUSINESS_ERROR.getCode(), e.getMessage(), this.getTrackExceptionMessage(e));
    }


    /**
     * SpringBoot RequestBody validate校验异常捕获
     *
     * @param e 异常
     * @return 标准返回值
     */
    @ExceptionHandler({MethodArgumentNotValidException.class})
    public BaseResponse validException(MethodArgumentNotValidException e) {
        log.error(e.getMessage(), e);
        FieldError fieldError = e.getBindingResult().getFieldError();
        if (fieldError != null && StringUtils.isNotEmpty(fieldError.getDefaultMessage())) {
            return new BaseResponse(ResultCodeEnum.PARAMETER_ERROR.getCode(), fieldError.getDefaultMessage(), this.getTrackExceptionMessage(e));
        }
        return new BaseResponse(ResultCodeEnum.PARAMETER_ERROR.getCode(), ResultCodeEnum.PARAMETER_ERROR.getMessage(), this.getTrackExceptionMessage(e));
    }

    /**
     * SpringBoot 参数列表字段validate校验异常捕获
     *
     * @param e 异常
     * @return 标准返回值
     */
    @ExceptionHandler({ConstraintViolationException.class})
    public BaseResponse constraintViolationException(ConstraintViolationException e) {
        log.error(e.getMessage(), e);
        StringBuilder messageBuilder = new StringBuilder();
        // 拼装异常信息
        e.getConstraintViolations().forEach(constraintViolation -> messageBuilder.append(constraintViolation.getMessageTemplate()).append(";"));
        return new BaseResponse(ResultCodeEnum.PARAMETER_ERROR.getCode(), messageBuilder.toString(), this.getTrackExceptionMessage(e));
    }


//    /**
//     * 未知异常捕获
//     *
//     * @param response 响应
//     * @param e        异常
//     * @return 标准返回值
//     */
//    @ExceptionHandler(BadCredentialsException.class)
//    public BaseResponse badCredentialsExceptionnHandler(HttpServletResponse response, Exception e) {
//        response.setStatus(HttpStatus.UNAUTHORIZED.value());
//        log.error(e.getMessage(), e);
//        return new BaseResponse(HttpStatus.UNAUTHORIZED.value(), this.getExceptionMessage(e, ResultCodeEnum.OTHER_EXCEPTION), this.getTrackExceptionMessage(e));
//    }

    /**
     * 获取异常描述
     *
     * @param e 异常
     * @return 异常 Message
     */
    private String getExceptionMessage(Exception e, ResultCodeEnum resultCodeEnum) {
        if (this.traceException) {
            return e.getMessage();
        }
        return resultCodeEnum.getMessage();
    }

    /**
     * 获取异常堆栈
     *
     * @param e 异常
     * @return 异常堆栈信息
     */
    private String getTrackExceptionMessage(Exception e) {
        if (this.traceException) {
            return ExceptionUtils.getStackTrace(e);
        }
        return "";
    }


}
